package com.zhike.service.file.impl;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;

import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;

import org.liuxp.minioplus.api.StorageService;
import org.liuxp.minioplus.api.model.vo.FileMetadataInfoVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.eva.epc.common.util.CommonUtils;
import com.google.gson.Gson;
import com.zhike.common.conf.IMBaseConf;
import com.zhike.common.dto.GroupInfo4Cache;
import com.zhike.common.enums.ResultCode;
import com.zhike.common.exception.ZkimException;
import com.zhike.common.repository.FileUploadLog;
import com.zhike.common.vo.UserInfo4Cache;
import com.zhike.mapper.user.UsersMapper;
import com.zhike.service.file.AttachmentUploader;
import com.zhike.service.file.FileBase;
import com.zhike.service.file.GroupAvatarUploader;
import com.zhike.service.file.IFileService;
import com.zhike.service.file.MsgImageUploader;
import com.zhike.service.file.MsgVoiceUploader;
import com.zhike.service.file.MyPhotoUploder;
import com.zhike.service.file.MyVoiceUploader;
import com.zhike.service.file.ShortVideoThumbUploader;
import com.zhike.service.file.UserAvatarUploader;
import com.zhike.service.store.AliStorage;
import com.zhike.service.system.ISystemConfigService;
import com.zhike.utils.cache.GroupsInfoCacheProvider;
import com.zhike.utils.cache.UsersInfoCacheProvider;
import com.zhike.utils.file.FileUtil;
import com.zhike.utils.file.httpfile.FileDownload;
import com.zhike.utils.file.httpfile.FileUpload;

import lombok.extern.slf4j.Slf4j;

@Service
@Slf4j
public class FileServiceImpl extends FileBase implements IFileService {

  @Resource
  private UsersMapper usersMapper;

  @Resource
  private IMBaseConf baseConf;

  @Autowired
  private MsgImageUploader msgImageUploader;

  @Autowired
  private MsgVoiceUploader msgVoiceUploader;

  @Autowired
  private MyVoiceUploader myVoiceUploader;

  @Autowired
  private AttachmentUploader attachmentUploader;

  @Autowired
  private UserAvatarUploader userAvatarUploader;

  @Autowired
  private GroupAvatarUploader groupAvatarUploader;

  @Autowired
  private MyPhotoUploder myPhotoUploder;

  @Autowired
  private ShortVideoThumbUploader shortVideoThumbUploader;

  @Resource
  private FileUpload fileUpload;

  @Autowired
  private MongoTemplate mongoTemplate;

  @Autowired
  private StorageService storageService;

  @Autowired
  private UsersInfoCacheProvider usersInfoCacheProvider;

  @Autowired
  private GroupsInfoCacheProvider groupsInfoCacheProvider;

  @Autowired
  private ISystemConfigService systemConfigService;

  @Override
  public HashMap<String, Object> userAvatarUploader(MultipartFile file, String uid)
      throws ServletException, IOException {
    return commonUploader(userAvatarUploader, file, uid);
  }

  @Override
  public HashMap<String, Object> groupAvatarUploader(MultipartFile file, String uid, String groupId)
      throws ServletException, IOException {
    return commonUploader(groupAvatarUploader, file, uid, groupId);
  }

  @Override
  public HashMap<String, Object> myPhotoUploader(MultipartFile file, String uid)
      throws ServletException, IOException {
    return commonUploader(myPhotoUploder, file, uid);
  }

  @Override
  public HashMap<String, Object> myVoiceUploader(MultipartFile file, String uid)
      throws ServletException, IOException {
    return commonUploader(myVoiceUploader, file, uid);
  }

  @Override
  public HashMap<String, Object> msgVoiceUploader(MultipartFile file, String uid)
      throws ServletException, IOException {
    return commonUploader(msgVoiceUploader, file, uid);
  }

  @Override
  public HashMap<String, Object> msgImageUploader(MultipartFile file, String uid)
      throws ServletException, IOException {
    return commonUploader(msgImageUploader, file, uid);
  }

  @Override
  public HashMap<String, Object> shortVideoUploader(MultipartFile file, String uid)
      throws ServletException, IOException {
    return commonUploader(shortVideoThumbUploader, file, uid);
  }

  /** 一个1像素透明图片 */
  public final static String ONE_PIXEL_TRANSPARENT_AVATAR_FILE_NAME = "on_pixel_transparent_avatar.png";

  /**
   * 用户头像HTTP下载实现方法.
   *
   * @param res
   * @param userUid
   * @param userLocalCachedAvatarFileName 用户缓存在本地的头像文件名（本参数只在enforceDawnload==false时有意义）
   * @param enforceDawnload               true表示无论客户端有无提交缓存图片名称本方法都将无条件返回该用户头像（如果头像确实存在的话），否则
   *                                      将据客户端提交上来的可能的本地缓存文件来判断是否需要下载用户头像（用户头像没有被更新过当然就不需要下载了！）
   * @param onePixelTransparentIfNo       此参数为“true”时，表示当用户没有设置头像时，会默认返回一个1像素透明图片（这主要用于Web网
   *                                      页里显示用户头像时，否则当用户没有设置头像时，因为没有图片数据返回而会在页面上显示一个“破图”img元素，那就难看死了！）
   * @throws IOException
   */
  private void processAvatarDownload(HttpServletResponse res, String userUid, String userLocalCachedAvatarFileName,
      boolean enforceDawnload, boolean onePixelTransparentIfNo) throws IOException {
    String avatarCurrentFileName = null;
    try {
      avatarCurrentFileName = usersMapper.selectUserAvatar(userUid);

      boolean avatarNotExsitsOrNotSetup = true;

      // 头像文件名不为空，即表示此用户设置过头像，为用户下载此头像文件即可
      if (!CommonUtils.isStringEmpty(avatarCurrentFileName, true)) {
        log.info("[HTTP]用户【" + userUid + "】的头像已被设置过，头像文件名：" + avatarCurrentFileName);

        // 如果不是强迫下载（即客户端有缓存机制情况但看服务端头像是否更新的情况）
        if (!enforceDawnload && userLocalCachedAvatarFileName != null) {
          // 客户端当前缓存着的用户头像跟数据库里是一样的，就意味着该用户没有更新过头像，不需要重新返回逻
          if (userLocalCachedAvatarFileName.toLowerCase().equals(avatarCurrentFileName)) {
            log.info("[HTTP]用户【" + userUid + "】缓存在本地的头像" + avatarCurrentFileName + "已是最新的，无需下载.");
            avatarNotExsitsOrNotSetup = false;
            return;
          }
        }

        String fileFullPath = baseConf.getDIR_USER_AVATAR_UPLOAD() + avatarCurrentFileName;

        if (!com.eva.epc.common.file.FileHelper.isFileExist(fileFullPath) && onePixelTransparentIfNo) {
          avatarNotExsitsOrNotSetup = true;
          log.info("[HTTP]用户【" + userUid + "】的头像文件" + avatarCurrentFileName
              + "并不存在！接下将提供默认的1像素透明图片作为默认图，保证web端显示时不会太丑！");
        } else {
          log.info("[HTTP]用户【" + userUid + "】缓存在本地的头像" + userLocalCachedAvatarFileName
              + "(null表示用户本地无缓存)需要更新，下载立即开始 ......");
          // 实施用户头像下载
          FileDownload.download(res, baseConf.getDIR_USER_AVATAR_UPLOAD() + avatarCurrentFileName,
              avatarCurrentFileName, FileDownload.CONTENT_TYPE_JPEG, false);

          avatarNotExsitsOrNotSetup = false;

          return;
        }
      }

      // 此用户没有设置过头像或者已设置过但头像文件不存在于服务器磁盘上（这一般是迁移数据时忘记迁移图片文件了）
      if (avatarNotExsitsOrNotSetup)
      // else
      {
        log.warn("[HTTP]对不起,用户【" + userUid + "】的头像(avatarCurrentFileName=" + avatarCurrentFileName + ")不存在！");

        // 此参数为“true”时，表示当用户没有设置头像时，会默认返回一个1像素透明图片（这主要
        // 用于Web网页里显示用户头像时，否则当用户没有设置头像时，因为没有图片数据返回而会
        // 在页面上显示一个“破图”img元素，那就难看死了！）
        if (onePixelTransparentIfNo) {
          File onePixelTransparentAvatarImg = new File(FileServiceImpl.class
              .getResource(ONE_PIXEL_TRANSPARENT_AVATAR_FILE_NAME).toURI());
          FileDownload.download(res, onePixelTransparentAvatarImg, ONE_PIXEL_TRANSPARENT_AVATAR_FILE_NAME,
              FileDownload.CONTENT_TYPE_JPEG, false);
        }
      }
    } catch (Exception e) {
      log.warn("[HTTP]用户【" + userUid + "】的头像文件不存在，本次下载已提前结束(" + e.getMessage() + ")");
    }
  }

  @Override
  public List<FileUploadLog> selectMyFiles(String uid, String fileName) {
    Criteria criteria = Criteria.where("uid").is(uid);
    Query query = new Query(criteria);
    if (StringUtils.isNotBlank(fileName)) {
      criteria.and("fileName").regex(".*?" + fileName + ".*$");
    }
    return mongoTemplate.find(query, FileUploadLog.class);
  }

  @Override
  public HashMap<String, Object> fileAttachment(MultipartFile file, String fileName)
      throws ServletException, IOException {
    return commonUploader(attachmentUploader, file, fileName);
  }

  @Override
  public void userAvatarDown(String uid, HttpServletResponse res) throws IOException {
    UserInfo4Cache uc = usersInfoCacheProvider.get(uid);
    if (uc == null) {
      throw new ZkimException(ResultCode.USER_NOT_EXSIT);
    }

    if (StringUtils.isBlank(uc.getAvatar())) {
      return;
    }
    // inline ：将文件内容直接显示在页面; attachment：弹出对话框让用户下载
    res.setHeader("Content-disposition", "inline");

    String storageDefault = systemConfigService.getValueByKey("storage", "default");
    String downloadUrl = "";
    if (storageDefault.equals("minio")) {
      downloadUrl = storageService.download(uc.getAvatar(), uid);
    } else if (storageDefault.equals("aliyun")) {
      downloadUrl = convertDownloadUrl(uc.getAvatar());
    }

    FileUtil.downloadWithUrl(downloadUrl, res);
  }

  @Override
  public void groupAvatarDown(String gid, HttpServletResponse res) {
    GroupInfo4Cache gc = groupsInfoCacheProvider.get(gid);
    if (StringUtils.isBlank(gc.getAvatar())) {
      throw new ZkimException(ResultCode.USER_AVATAR_EMPTY);
    }
    // inline ：将文件内容直接显示在页面; attachment：弹出对话框让用户下载
    res.setHeader("Content-disposition", "inline");
    String storageDefault = systemConfigService.getValueByKey("storage", "default");
    String downloadUrl = "";
    if (storageDefault.equals("minio")) {
      downloadUrl = storageService.download(gc.getAvatar(), gid);
    } else if (storageDefault.equals("aliyun")) {
      downloadUrl = convertDownloadUrl(gc.getAvatar());
    }
    FileUtil.downloadWithUrl(downloadUrl, res);
  }

  @Override
  public String getAttachmentDownloadUrl(String fileKey, HttpServletResponse res) {
    String download = storageService.download(fileKey, null);
    //拼接fullName
    if (StringUtils.isNotBlank(download)) {
      FileMetadataInfoVo one = storageService.one(fileKey);
      download += "&fullfilename=" + one.getFileName();
    }
    return download;
  }

  private String convertDownloadUrl(String avatarPath) {
    String aliyunConfigJsonStr = systemConfigService.getValueByKey("storage", "aliyun");
    if (StringUtils.isNotBlank(aliyunConfigJsonStr)) {
      AliStorage storage = new Gson().fromJson(aliyunConfigJsonStr, AliStorage.class);
      String protocol = storage.getSsl() ? "https://" : "http://";
      return protocol + storage.getDomain() + avatarPath;
    }
    return "";
  }

}
